/**
 *
 */
package de.cbf.cam.dsa.person.extension;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.osgi.framework.BundleContext;

import de.cbf.cam.dsa.person.extension.interfaces.IBirthDataGenerator;
import de.cbf.cam.dsa.person.extension.interfaces.INameGenerator;
import de.cbf.cam.dsa.person.extension.interfaces.IPersonSpecialsGenerator;
import de.cbf.cam.dsa.person.extension.interfaces.IRCPGenerator;

/**
 * @author salter
 *
 */
public class PersonExtensionPointResovler {
	private static final Log logger = LogFactory.getLog(PersonExtensionPointResovler.class);

	private static final String EXTENSION_POINT_NAME_GENERATOR_ID 			= "de.cbf.cam.dsa.person.name_generator";
	private static final String EXTENSION_POINT_BIRTH_DATA_GENERATOR_ID 	= "de.cbf.cam.dsa.person.birth_data_generator";
	private static final String EXTENSION_POINT_RCP_GENERATOR_ID 			= "de.cbf.cam.dsa.person.rcp_generator";
	private static final String EXTENSION_POINT_PERSON_SPECIAL_GENERATOR_ID	= "de.cbf.cam.dsa.person.specials_generator";

	protected static PersonExtensionPointResovler instance;

	protected BundleContext context;

	protected Map<String,INameGenerator> 			nameGeneratorsMap;
	protected Map<String,IBirthDataGenerator> 		birthDataGeneratorsMap;
	protected Map<String,IRCPGenerator> 			rcpGeneratorsMap;
	protected Map<String,IPersonSpecialsGenerator> 	specialsGeneratorsMap;

	protected String preferredNameGenerator 			= null;
	protected String preferredBirthDataGenerator 		= null;
	protected String preferredRCPGenerator 				= null;
	protected String preferredPersonSpecialsGenerator 	= null;
	/**
	 *
	 */
	private PersonExtensionPointResovler() {
		nameGeneratorsMap 		= new HashMap<String, INameGenerator>();
		birthDataGeneratorsMap 	= new HashMap<String, IBirthDataGenerator>();
		rcpGeneratorsMap		= new HashMap<String, IRCPGenerator>();;
		specialsGeneratorsMap 	= new HashMap<String, IPersonSpecialsGenerator>();
	}

	public static PersonExtensionPointResovler getInstance() {
		if(instance == null){
			instance = new PersonExtensionPointResovler();
		}
		return instance;
	}

	public void setBundleContext(BundleContext context){
		this.context = context;
	}

	protected void evaluateExtensions(BundleContext context) {
		logger.debug("evaluating extensions...");
		evaluateExtendedNameGenerators();
		evaluateExtendedBirthDataGenerators();
		evaluateExtendedRCPGenerators();
		logger.debug("evaluating extensions finished");
	}



	protected void evaluateExtendedNameGenerators() {
		logger.trace("Evaluating fName generator extensions");
		//
		IConfigurationElement[] elements =  Platform.getExtensionRegistry().
		  getConfigurationElementsFor(EXTENSION_POINT_NAME_GENERATOR_ID);

		try {
			for (IConfigurationElement e : elements) {
		  		logger.trace("Evaluating fName generator extension: "+e);

		        final Object object = e.createExecutableExtension("class");
		        if(object == null){
		        	logger.error("ExecutableExtension not created, check IExecutableExtensionFactory implementation for create()");
		        }
		        if (object instanceof INameGenerator) {
		        	INameGenerator generator = (INameGenerator)object;
		        	executeNameGeneratorExtension(generator);
		        }else{
					logger.error( "Wrong class of object returned("+object.getClass().getName()+"),/n" +
		        			"would need "+INameGenerator.class.getName());
		        }
			}
		} catch (CoreException ex) {
			logger.error(ex.getMessage());
		}
	}



	protected void evaluateExtendedBirthDataGenerators() {
		logger.trace("Evaluating birth data generator extensions");
		//
		IConfigurationElement[] elements =  Platform.getExtensionRegistry().
		  getConfigurationElementsFor(EXTENSION_POINT_BIRTH_DATA_GENERATOR_ID);

		try {
			for (IConfigurationElement e : elements) {
		  		logger.trace("Evaluating birth data generator extension: "+e);

		        final Object object = e.createExecutableExtension("class");
		        if(object == null){
		        	logger.error("ExecutableExtension not created, check IExecutableExtensionFactory implementation for create()");
		        }
		        if (object instanceof IBirthDataGenerator) {
		        	IBirthDataGenerator generator = (IBirthDataGenerator)object;
		        	executeBirthDataGeneratorExtension(generator);
		        }else{
					logger.error( "Wrong class of object returned("+object.getClass().getName()+"),/n" +
		        			"would need "+IBirthDataGenerator.class.getName());
		        }
			}
		} catch (CoreException ex) {
			logger.error(ex.getMessage());
		}
	}

	protected void evaluateExtendedRCPGenerators() {
		logger.trace("Evaluating rcp generator extensions");
		//
		IConfigurationElement[] elements =  Platform.getExtensionRegistry().
		  getConfigurationElementsFor(EXTENSION_POINT_RCP_GENERATOR_ID);

		try {
			for (IConfigurationElement e : elements) {
		  		logger.trace("Evaluating rcp generator extension: "+e);

		        final Object object = e.createExecutableExtension("class");
		        if(object == null){
		        	logger.error("ExecutableExtension not created, check IExecutableExtensionFactory implementation for create()");
		        }
		        if (object instanceof IRCPGenerator) {
		        	IRCPGenerator generator = (IRCPGenerator)object;
		        	executeRCPGeneratorExtension(generator);
		        }else{
					logger.error( "Wrong class of object returned("+object.getClass().getName()+"),/n" +
		        			"would need "+IRCPGenerator.class.getName());
		        }
			}
		} catch (CoreException ex) {
			logger.error(ex.getMessage());
		}
	}

	protected void evaluateExtendedPersonSpecialsGenerators() {
		logger.trace("Evaluating person special generator extensions");
		//
		IConfigurationElement[] elements =  Platform.getExtensionRegistry().
		  getConfigurationElementsFor(EXTENSION_POINT_PERSON_SPECIAL_GENERATOR_ID);

		try {
			for (IConfigurationElement e : elements) {
		  		logger.trace("Evaluating person special generator extension: "+e);

		        final Object object = e.createExecutableExtension("class");
		        if(object == null){
		        	logger.error("ExecutableExtension not created, check IExecutableExtensionFactory implementation for create()");
		        }
		        if (object instanceof IPersonSpecialsGenerator) {
		        	IPersonSpecialsGenerator generator = (IPersonSpecialsGenerator)object;
		        	executePersonSpecialsGeneratorExtension(generator);
		        }else{
					logger.error( "Wrong class of object returned("+object.getClass().getName()+"),/n" +
		        			"would need "+IPersonSpecialsGenerator.class.getName());
		        }
			}
		} catch (CoreException ex) {
			logger.error(ex.getMessage());
		}
	}



	protected void executeNameGeneratorExtension(final INameGenerator generator) {
	    ISafeRunnable runnable = new ISafeRunnable() {
	    	@Override
	    	public void handleException(Throwable e) {
	    		logger.error("Exception in running extension");
	    	}

	    	@Override
	    	public void run() throws Exception {
	    		addNameGenerator(generator);
	    	}
	    };
	    SafeRunner.run(runnable);
	}

	protected void executeBirthDataGeneratorExtension(final IBirthDataGenerator generator) {
		ISafeRunnable runnable = new ISafeRunnable() {
	    	@Override
	    	public void handleException(Throwable e) {
	    		logger.error("Exception in running extension");
	    	}

	    	@Override
	    	public void run() throws Exception {
	    		addBirthDataGenerator(generator);
	    	}
	    };
	    SafeRunner.run(runnable);
	}

	protected void executeRCPGeneratorExtension(final IRCPGenerator generator) {
		ISafeRunnable runnable = new ISafeRunnable() {
	    	@Override
	    	public void handleException(Throwable e) {
	    		logger.error("Exception in running extension");
	    	}

	    	@Override
	    	public void run() throws Exception {
	    		addRCPGenerator(generator);
	    	}
	    };
	    SafeRunner.run(runnable);
	}

	protected void executePersonSpecialsGeneratorExtension(final IPersonSpecialsGenerator generator) {
		ISafeRunnable runnable = new ISafeRunnable() {
	    	@Override
	    	public void handleException(Throwable e) {
	    		logger.error("Exception in running extension");
	    	}

	    	@Override
	    	public void run() throws Exception {
	    		addSpecialsGenerator(generator);
	    	}
	    };
	    SafeRunner.run(runnable);
	}

	public INameGenerator getNameGenerator() {
		preferredNameGenerator = getPreferredNameGenerator();
		if(preferredNameGenerator == null){
			evaluateExtendedNameGenerators();
		}
		return nameGeneratorsMap.get(preferredNameGenerator);
	}

	public void addNameGenerator(INameGenerator nameGenerator) {
		logger.debug("adding name generator: "+nameGenerator);
		if(nameGeneratorsMap.isEmpty()){
			logger.debug("setting preference to it");
			setPreferredNameGenerator(nameGenerator.getName());
		}
		this.nameGeneratorsMap.put(nameGenerator.getName(),nameGenerator);
	}

	public IBirthDataGenerator getBirthDataGenerator() {
		preferredBirthDataGenerator = getPreferredBirthDataGenerator();
		if(preferredBirthDataGenerator == null){
			evaluateExtendedBirthDataGenerators();
		}
		return birthDataGeneratorsMap.get(preferredBirthDataGenerator);
	}

	public void addBirthDataGenerator(IBirthDataGenerator birthDataGenerator) {
		logger.debug("adding birth data generator: "+birthDataGenerator);
		if(birthDataGeneratorsMap.isEmpty()){
			logger.debug("setting preference to it");
			setPreferredBirthDataGenerator(birthDataGenerator.getName());
		}
		this.birthDataGeneratorsMap.put(birthDataGenerator.getName(),birthDataGenerator);
	}

	public IRCPGenerator getRCPGenerator() {
		preferredRCPGenerator = getPreferredRCPGenerator();
		if(preferredRCPGenerator == null){
			evaluateExtendedRCPGenerators();
		}
		return rcpGeneratorsMap.get(preferredRCPGenerator);
	}

	public void addRCPGenerator(IRCPGenerator rcpGenerator) {
		logger.debug("adding rcp generator: "+rcpGenerator);
		if(rcpGeneratorsMap.isEmpty()){
			logger.debug("setting preference to it");
			preferredRCPGenerator = rcpGenerator.getName();
		}
		this.rcpGeneratorsMap.put(rcpGenerator.getName(),rcpGenerator);
	}

	public IPersonSpecialsGenerator getSpecialsGenerator() {
		preferredPersonSpecialsGenerator = getPreferredPersonSpecialsGenerator();
		if(preferredPersonSpecialsGenerator == null){
			evaluateExtendedPersonSpecialsGenerators();
		}
		return specialsGeneratorsMap.get(preferredPersonSpecialsGenerator);
	}


	public void addSpecialsGenerator(IPersonSpecialsGenerator specialsGenerator) {
		logger.debug("adding specials generator: "+specialsGenerator);
		if(specialsGeneratorsMap.isEmpty()){
			logger.debug("setting preference to it");
			preferredPersonSpecialsGenerator = specialsGenerator.getName();
		}
		this.specialsGeneratorsMap.put(specialsGenerator.getName(),specialsGenerator);
	}

	public String getPreferredNameGenerator() {
		return preferredNameGenerator;
	}

	public void setPreferredNameGenerator(String preferredNameGenerator) {
		this.preferredNameGenerator = preferredNameGenerator;
	}

	public String getPreferredBirthDataGenerator() {
		return preferredBirthDataGenerator;
	}

	public void setPreferredBirthDataGenerator(String preferredBirthDataGenerator) {
		this.preferredBirthDataGenerator = preferredBirthDataGenerator;
	}

	public String getPreferredRCPGenerator() {
		return preferredRCPGenerator;
	}

	public void setPreferredRCPGenerator(String preferredRCPGenerator) {
		this.preferredRCPGenerator = preferredRCPGenerator;
	}

	public String getPreferredPersonSpecialsGenerator() {
		return preferredPersonSpecialsGenerator;
	}

	public void setPreferredPersonSpecialsGenerator(
			String preferredPersonSpecialsGenerator) {
		this.preferredPersonSpecialsGenerator = preferredPersonSpecialsGenerator;
	}
}
